package com.example.inventory.thread;

import com.example.inventory.request.ProductInventoryCacheRefreshRequest;
import com.example.inventory.request.ProductInventoryDBUpdateRequest;
import com.example.inventory.request.Request;
import com.example.inventory.request.RequestQueue;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;

/**
 * 执行缓存更新或读取请求的龚总线程
 * @author james
 */
public class RequestProcessorThread implements Callable<Boolean> {

    ArrayBlockingQueue<Request> queue;

    public RequestProcessorThread(ArrayBlockingQueue<Request> queue) {
        this.queue = queue;
    }

    @Override
    public Boolean call() throws Exception {
        try {
            for (; ; ) {
                // ArrayBlockingQueue
                // blocking 阻塞队列, take()如果队列是空的会阻塞猪
                Request request = queue.take();
                boolean forceRefresh = request.isForceRefresh();
                System.out.println("消费者工作线程队列中获取到任务:" + request.getTaskName());

                // 读请求的去重
                if (!forceRefresh) {
                    RequestQueue requestQueue = RequestQueue.getInstance();
                    Map<Integer, Boolean> flagMap = requestQueue.getFlagMap();

                    if (request instanceof ProductInventoryDBUpdateRequest) {
                        // 如果是一个更新数据库的请求，那么就将那个productId对应的标识设置为true
                        flagMap.put(request.getProductId(), true);
                    } else if (request instanceof ProductInventoryCacheRefreshRequest) {
                        // 如果是一个读取数据集并更新缓存的请求,
                        Boolean flag = flagMap.get(request.getProductId());
                        // 如果 flag == null 代表前面没有人在更新缓存,
                        // 那么在 flag里设置 一个标记为false, 如此后面的读取数据库并更新缓存的请求可以忽略,
                        // 因为当前线程将这个事做了
                        if (Objects.isNull(flag)) {
                            flagMap.put(request.getProductId(), Boolean.FALSE);
                        }

                        // 如果是flag是true, 前面有一个[更新数据库] 的请求在里边
                        // 因为[读数据库并更新缓存]只会设置为false, 设置为true的只有[更新数据库]
                        if (Objects.nonNull(flag) && flag) {
                            // 所以我这里也帮他设置为false,因为后面的[读数据库并更新缓存] 不用做啦
                            // 因为当前线程, 也就是我帮你去[读数据库并更新缓存]
                            flagMap.put(request.getProductId(), Boolean.FALSE);
                        }

                        // 如果是缓存刷新的请求，而且发现标识不为空，但是标识是false
                        // 说明前面已经有一个数据库更新请求+一个缓存刷新请求了，大家想一想
                        if (Objects.nonNull(flag) && !flag) {
                            // return Boolean.TRUE;
                            continue;
                        }
                    }
                }

                // System.out.println("===========日志===========: 工作线程处理请求，商品id=" + request.getProductId());
                System.err.println("===========日志===========: 消费者工作线程处理请求，任务是["+request.getTaskName()+"]");
                //  来到这里 代表是需要[读数据库并更新缓存]的请求
                request.process();

                // 假如说, 执行完一个读请求之后,假设数据已经刷新到redis中
                // 但是后面可能redis的数据会因为内存满了, 被自动清理掉
                // 如果说数据从redis中被自动清理掉以后,然后后面来了一个读请求,此时如果进来,
                // 发现标志位是false,就不会去执行这个刷新操作
                // 所以在执行完这个读请求后, 实际上这个标志位是停留在false的
                // 尴尬的不行, 因为刚刚才有人读, 结果缓存里没有这数据

                /** 处理方法 {@link com.example.inventory.controller.ProductInventoryController#getProductInventory}
                 * 如果缓存读不到,则到数据库中读取,
                 * 并且发送一个强刷缓存的任务到队列中
                 */

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Boolean.TRUE;
    }

    /**
     * 下面是老师那里复制过来了的, 我修复了 [线程退出导致任务队列没人处理消息的bug]
     * @return
     * @throws Exception

     @Override public Boolean call() throws Exception {
     try {
     while (true) {
     // ArrayBlockingQueue
     // Blocking就是说明，如果队列满了，或者是空的，那么都会在执行操作的时候，阻塞住
     Request request = queue.take();
     boolean forceRfresh = request.isForceRefresh();
     System.out.println("===========日志===========: 工作线程  接收到  请求，商品id=" + request.getProductId());
     boolean reflush = true;
     // 先做读请求的去重
     if (!forceRfresh) {
     RequestQueue requestQueue = RequestQueue.getInstance();
     Map<Integer, Boolean> flagMap = requestQueue.getFlagMap();

     // if(request instanceof ProductInventoryDBUpdateRequest) {
     //     如果是一个更新数据库的请求，那么就将那个productId对应的标识设置为true
     // flagMap.put(request.getProductId(), true);
     // } else
     if (request instanceof ProductInventoryCacheRefreshRequest) {
     Boolean flag = flagMap.get(request.getProductId());

     // 如果flag是null
     if (flag == null) {
     flagMap.put(request.getProductId(), false);
     }

     // 如果是缓存刷新的请求，那么就判断，如果标识不为空，而且是true，就说明之前有一个这个商品的数据库更新请求
     if (flag != null && flag) {
     flagMap.put(request.getProductId(), false);
     }

     // 如果是缓存刷新的请求，而且发现标识不为空，但是标识是false
     // 说明前面已经有一个数据库更新请求+一个缓存刷新请求了，大家想一想
     if (flag != null && !flag) {
     // 对于这种读请求，直接就过滤掉，不要放到后面的内存队列里面去了
     // return true;
     reflush = false;
     }
     if (reflush){
     System.out.println("===========日志===========: 工作线程处理请求，商品id=" + request.getProductId());
     request.process();
     }
     }
     }else {
     // 强制刷新缓存
     System.out.println("===========日志===========: 工作线程处理请求，商品id=" + request.getProductId());
     request.process();
     }


     // 执行这个request操作

     // 假如说，执行完了一个读请求之后，假设数据已经刷新到redis中了
     // 但是后面可能redis中的数据会因为内存满了，被自动清理掉
     // 如果说数据从redis中被自动清理掉了以后
     // 然后后面又来一个读请求，此时如果进来，发现标志位是false，就不会去执行这个刷新的操作了
     // 所以在执行完这个读请求之后，实际上这个标志位是停留在false的
     }
     } catch (Exception e) {
     e.printStackTrace();
     }
     return true;
     }
     */
}
